/**
	Pulsar engine. Additional include file.
	Memory manager (release).

	Copyright (C) 2013-2014 Dmitry Dolgov

	This program is free software; you can redistribute it and/or modify it under the terms of the
	GNU General Public License as published by the Free Software Foundation; either version 2 of
	the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
	without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
	See the GNU General Public License for more details.
*/

namespace pl
{

const pl_uint64 PL_MAX_ALLOCATION_SIZE = 0x000000007FFFFFFFL;

/**
	Allocation options
	Works for PL_MEMORY_DEBUG, PL_MEMORY_RELEASE and PL_MEMORY_FINAL only
*/

const pl_uint32  MEMOPTIONS_ZEROALLOC_DEFAULT  = 0x00000000; // Default CRT defaviour
const pl_uint32  MEMOPTIONS_ZEROALLOC_MALLOC   = 0x00000001; // Allocate as regular memory
const pl_uint32  MEMOPTIONS_ZEROALLOC_STUB     = 0x00000002; // Always return stub address
const pl_uint32  MEMOPTIONS_ZEROALLOC_NULL     = 0x00000003; // Always return NULL
const pl_uint32  MEMOPTIONS_ZEROALLOC_BREAK    = 0x00000004; // Break into debugger
const pl_uint32  MEMOPTIONS_ZEROALLOC_MASK     = 0x00000007; // MASK for MEMOPTIONS_ZEROALLOC_* flags
const pl_uint32  MEMOPTIONS_FREEMODE_DEFAULT   = 0x00000000; // Default CRT defaviour
const pl_uint32  MEMOPTIONS_FREEMODE_NULLSKIP  = 0x00000010; // Ignore free(NULL)
const pl_uint32  MEMOPTIONS_FREEMODE_NULLBREAK = 0x00000020; // Break into debugger
const pl_uint32  MEMOPTIONS_FREEMODE_MASK      = 0x00000070; // MASK for MEMOPTIONS_FREEMODE_* flags
const pl_uint32  MEMOPTIONS_MEMALIGN_DEFAULT   = 0x00000000; // Default CRT defaviour
const pl_uint32  MEMOPTIONS_MEMALIGN_CANFREE   = 0x00000100; // memalign() may call standard free()
const pl_uint32  MEMOPTIONS_MEMALIGN_CANTFREE  = 0x00000200; // memalign() must call free_align()
const pl_uint32  MEMOPTIONS_MEMALIGN_MASK      = 0x00000700; // MASK for MEMOPTIONS_MEMALIGN_* flags
const pl_uint32  MEMOPTIONS_THREADS_DEFAULT    = 0x00000000; // Default CRT defaviour
const pl_uint32  MEMOPTIONS_THREADS_ENABLE     = 0x00001000; // Can alloc/free between threads
const pl_uint32  MEMOPTIONS_THREADS_DISABLE    = 0x00002000; // Can not alloc/free between threads
const pl_uint32  MEMOPTIONS_THREADS_MASK       = 0x00007000; // MASK for MEMOPTIONS_THREADS_* flags

/**
	Global allocation interface
*/
class IGlobalAlloc
{
public:
	virtual ~IGlobalAlloc() {}

	// Request to allocate memory (align = 0 means regular allocation)
	virtual void* onAlloc(size_t size, int align) = 0;
	// Request to free memory
	virtual void onFree(void* ptr) = 0;
	// Request to reallocate a block
	virtual void* onRealloc(void* ptr, size_t size) = 0;
	// Request to check a pointer
	virtual MemoryError onCheck(void* ptr)
	{
		return ptr ? MEMORY_ERROR_NONE : MEMORY_ERROR_NULL;
	}
	// Static allocation
	virtual void* onStaticAlloc(size_t size)
	{
		return onAlloc(size, 16);
	}
	// Statiuc deallocation
	virtual void onStaticFree(void* ptr)
	{
		onFree(ptr);
		return;
	}
};

// Internal allocations for PL_MEMORY_RELEASE / PL_MEMORY_FINAL
extern void* mallocRtl(size_t size);
extern void* callocRtl(size_t n, size_t size);
extern void* reallocRtl(void* p, size_t size);
extern void freeRtl(void* p);
extern char* strdupRtl(const char* str);
extern void* mallocAlignRtl(int align, size_t size);
extern void freeAlignRtl(void* p);
extern void* mallocStaticRtl(size_t size);
extern void freeStaticRtl(void* p);

} // namespace pl


// Translate allocations into internal calls
#if !defined(__PL_NO_MEMORY_MAPPING__) && !defined(PL_MEMORY_NONE)

#ifdef malloc
#undef malloc
#endif
#ifdef calloc
#undef calloc
#endif
#ifdef free
#undef free
#endif
#ifdef realloc
#undef realloc
#endif
#ifdef strdup
#undef strdup
#endif
#ifdef memalign
#undef memalign
#endif

#if defined(PL_MEMORY_DEBUG)

#define malloc(size)                pl::mallocDbg(size, pl::BLOCK_TYPE_MALLOC, 0, PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME)
#define calloc(n, size)             pl::callocDbg(n, size, PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME)
#define realloc(p, size)            pl::reallocDbg(p, size, PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME)
#define free(p)                     pl::freeDbg(p, pl::BLOCK_TYPE_MALLOC, PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME)
#define strdup(str)                 pl::strdupDbg(str, PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME)

#define PL_NEW new(PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME)
#define PL_DELETE delete

#elif defined (PL_MEMORY_RELEASE) || defined (PL_MEMORY_FINAL)

#define malloc(n)                   pl::mallocRtl(n)
#define calloc(n, size)             pl::callocRtl(n, size)
#define realloc(p, size)            pl::reallocRtl(p, size)
#define free(p)                     pl::freeRtl(p)
#define strdup(str)                 pl::strdupRtl(str)

#define PL_NEW new
#define PL_DELETE delete

#elif defined (PL_MEMORY_NONE)

// Do nothing

#else
#error "Wrong debug memory management type specified"
#endif

#else // __PL_NO_MEMORY_MAPPING__

#define PL_NEW ::new
#define PL_DELETE ::delete

#endif // __PL_NO_MEMORY_MAPPING__

#if defined(PL_MEMORY_DEBUG)

#define malloc_align(align, size)   pl::mallocDbg(size, pl::BLOCK_TYPE_MALLOC_ALIGN, align, PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME)
#define memalign(align, size)		pl::mallocDbg(size, pl::BLOCK_TYPE_MALLOC_ALIGN, align, PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME)
#define free_align(p)               pl::freeDbg(p, pl::BLOCK_TYPE_MALLOC_ALIGN, PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME)
#define malloc_static(size)         pl::mallocStaticDbg(size, PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME)
#define free_static(p)              pl::freeStaticDbg(p, PL_T(__FILE__), __LINE__, PL_FUNCTION_NAME)

#elif defined (PL_MEMORY_RELEASE) || defined (PL_MEMORY_FINAL)

#define malloc_align(align, size)   pl::mallocAlignRtl(align, size)
#define memalign(align, size)       pl::mallocAlignRtl(align, size)
#define free_align(p)               pl::freeAlignRtl(p)
#define malloc_static(size)         pl::mallocStaticRtl(size)
#define free_static(p)              pl::freeStaticRtl(p)

#elif defined (PL_MEMORY_NONE)

// Do nothing

#else
#error "Wrong debug memory management type specified"
#endif


#if defined(PL_MSC)
#include <pulsar/inc/msc/pulsar_memory_msc.h>
#elif defined(PL_GCC)
#include <pulsar/inc/gcc/pulsar_memory_gcc.h>
#else
#error "Wrong compiler type"
#endif

//
// End of file 'pulsar_memorymng.h'
//
